﻿#region << 版 本 注 释 >>
/*----------------------------------------------------------------
// Copyright (C) 2021 kingerrobot
// 版权所有。 
//
// 文件名：BWcsEquipLocHold
// 文件功能描述：
//
// 创建者：名字 (zzl)
// 时间：2021/12/13 11:27:05
//
// 修改人：
// 时间：
// 修改说明：
// 版本：V1.0.0
//----------------------------------------------------------------*/
#endregion
using KingerRobot.MexicoSailunMDC.Dao;
using KingerRobot.MexicoSailunMDC.Spi;
using System.Diagnostics;
using System.Linq;

namespace KingerRobot.MexicoSailunMDC.SpiService;

internal class BWcsEquipLocHold : IWcsEquipLocHold
{
    private readonly static ITask _Task = new BTask();
    private readonly static IRcsLocation _RcsLocation = new BRcsLocation();
    private readonly static IWcsAsrsCrnInput _WcsAsrsCrnInput = new BWcsAsrsCrnInput();
    private readonly static IWcsAsrsLocHalt _WcsAsrsLocHalt = new BWcsAsrsLocHalt();
    private readonly static IRcsEquipCrn _RcsEquipCrn = new BRcsEquipCrn();
    private readonly static IProdProduct _ProdProduct = new BProdProduct();
    private readonly static IWmsSetting _WmsSetting = new BWmsSetting();

    public List<POWcsEquipLocHold> GetEquipLocHolds()
    {
        var db = Db.Default;

        return db.VWcsEquipLocHolds.Select(t => new POWcsEquipLocHold
        {
            Id = t.Id,
            AreaId = t.AreaId,
            CreateTime = t.CreateTime,
            EquipId = t.EquipId,
            LocId = t.LocId,
            RelateScore = t.RelateScore,
            UpdateTime = t.UpdateTime,
            UseFlag = t.UseFlag,
        }).OrderBy(t => t.RelateScore).ToList();
    }

    public POWcsEquipLocHold GetEquipLocHoldByLoc(long locId)
    {
        var db = Db.Default;
        var result = db.WcsEquipLocHolds.Where(t => t.LocId == locId).FirstOrDefault();
        return result;
    }

    /// <summary>
    /// 入库 空货位【入库前，入库选择库位】
    /// </summary>
    /// <param name="isTask"></param>
    /// <returns></returns>
    public List<POWcsEquipLocHold> GetNoGoodsEquipLocHoldsByCrn(List<CrnForkInfo> usableCrnForkInfos,
        List<CrnForkInfo> abnormalCrnForkInfos,
        List<PORcsLocation> locations, bool isTask = true)
    {
        // 无异常
        var usableCrnIds = usableCrnForkInfos
            .Select(t => t.CrnId)
            .Distinct()
            .ToList();

        // 异常
        var abnormalForks = abnormalCrnForkInfos
            .Select(t => t.Fork)
            .Distinct()
            .ToList();

        // 所有堆垛机管理的货位
        var equipLocHold = GetEquipLocHolds();
        equipLocHold = equipLocHold.Join(usableCrnIds, f => f.EquipId, s => s, (f, s) => f).ToList();
        //.Where(t => usableCrnIds.Contains(t.EquipId)).ToList();




        // 所有堆垛机管理的空货位
        GetEquipLocHoldNoGoods(ref equipLocHold, locations);

        // 去掉货叉损坏区后不可用空货位
        GetEquipLocHaltCrn(ref equipLocHold, abnormalForks);

        // 任务目标放货位汇总数量
        var taskGroups = _Task.GetTargetLocTaskGroup();

        // 去掉不满足高度的货位
        //GetLocHeight(ref equipLocHold, locations, height);
        //if (equipLocHold.Count == 0)
        //{
        //    return -5;
        //}


        // 空货位去掉 入库任务还没有到达的（具体立库货位）的数量
        GetEquipLocHoldNoLocTasks(ref equipLocHold, taskGroups);

        if (isTask)
        {
            // 空货位去掉 输送任务还没有到达的（到堆垛机站台位）
            GetEquipLocHoldNoTargetTasks(ref equipLocHold, taskGroups);
        }

        return equipLocHold;
    }



    /// <summary>
    /// 入库 空货位【入库前，入库选择库位】
    /// </summary>
    /// <param name="isTask"></param>
    /// <returns></returns>
    public List<POWcsEquipLocHold> GetNoGoodsEquipLocHoldsByCrn(List<CrnForkInfo> usableCrnForkInfos,
        List<CrnForkInfo> abnormalCrnForkInfos,
        List<PORcsLocation> locations, List<POWcsEquipLocHold> equipLocHold)
    {

        // 无异常
        var usableCrnIds = usableCrnForkInfos
            .Select(t => t.CrnId)
            .Distinct()
            .ToList();

        // 异常
        var abnormalForks = abnormalCrnForkInfos
            .Select(t => t.Fork)
            .Distinct()
            .ToList();
        //只筛选双空库位
       // locations = _RcsLocation.GetDoubleRcsLocation(locations);

        // 所有堆垛机管理的货位
        // var equipLocHold = GetEquipLocHolds();
        equipLocHold = equipLocHold.Join(usableCrnIds, f => f.EquipId, s => s, (f, s) => f).ToList();
        // 所有堆垛机管理的空货位
        GetEquipLocHoldNoGoods(ref equipLocHold, locations);
        // 去掉货叉损坏区后不可用空货位
        GetEquipLocHaltCrn(ref equipLocHold, abnormalForks);

        // 任务目标放货位汇总数量
        var taskGroups = _Task.GetTargetLocTaskGroup();

        //去掉不满足高度的货位
        //var wmsMaterialHeight = _WmsSetting.GetWmsSettingByCode(ConstFields.MaterialHeightSideType);
        //int.TryParse(wmsMaterialHeight?.Value, out int settingHeight);
        //GetLocHeightV2(ref equipLocHold, locations, height, outSide);




        // 空货位去掉 入库任务还没有到达的（具体立库货位）的数量
        GetEquipLocHoldNoLocTasks(ref equipLocHold, taskGroups);
        //选堆垛机的时候 预留两个空货位

        var reserve = 0;
        //var wmsReserve = _WmsSetting.GetWmsSettingByCode(ConstFields.CrnReserveLocation);
        //int.TryParse(wmsReserve?.Value, out reserve);

        // 空货位去掉 输送任务还没有到达的（到堆垛机站台位）
        GetEquipLocHoldNoTargetTasks(ref equipLocHold, taskGroups, reserve);

        //空货位去掉 单个库位  只能选择一组两个双空库位


        return equipLocHold;
    }


   

    /// <summary>
    /// 出库 有货位
    /// </summary>
    /// <returns></returns>
    public List<POWcsEquipLocHold> GetGoodsEquipLocHolds(List<CrnForkInfo> crnForkInfos)
    {
        // 无异常
        var crnIds = crnForkInfos
            .Where(t => t.IsEnable == true)
            .Select(t => t.CrnId)
            .Distinct()
            .ToList();
        // 异常
        var abnormalForks = crnForkInfos
            .Where(t => t.IsEnable == false)
            .Select(t => t.Fork)
            .Distinct()
            .ToList();

        Stopwatch stopwatch = Stopwatch.StartNew();
        // 所有堆垛机管理的货位
        var equipLocHold = GetEquipLocHolds();
        equipLocHold = equipLocHold.Join(crnIds, f => f.EquipId, s => s, (f, s) => f).ToList();
        //.Where(t => crnIds.Contains(t.EquipId)).ToList();

        // 所有堆垛机管理的有货货位
        GetEquipLocHoldGoods(ref equipLocHold);

        // 只能产品质量为： N 的出库
        GetProductNotN(ref equipLocHold);

        // 去掉货叉损坏区域后可用货位
        //OLDTODO 【已解决】abnormalForks 单独查异常
        GetEquipLocHaltCrn(ref equipLocHold, abnormalForks);

        // 有货货位去掉任务还没出库完成（立库货位到出库站台位任务）
        var taskGroupCounts = _Task.GetStartLocTaskGroup(); // 任务开始货位汇总数量

        GetEquipLocHoldNoLocTasks(ref equipLocHold, taskGroupCounts);

        return equipLocHold;
    }


    /// <summary>
    /// 出库 有货位
    /// </summary>
    /// <returns></returns>
    public List<POWcsEquipLocHold> GetGoodsEquipLocHoldsV2(List<CrnForkInfo> crnForkInfos, List<ProductLocBean> productN, List<POWcsEquipLocHold> wcsEquipLocHolds)
    {
        // 无异常
        var crnIds = crnForkInfos
            .Where(t => t.IsEnable == true)
            .Select(t => t.CrnId)
            .Distinct()
            .ToList();
        // 异常
        var abnormalForks = crnForkInfos
            .Where(t => t.IsEnable == false)
            .Select(t => t.Fork)
            .Distinct()
            .ToList();

        // 所有堆垛机管理的货位
        var equipLocHold = wcsEquipLocHolds.Where(t => crnIds.Contains(t.EquipId)).ToList();


        // 所有堆垛机管理的有货货位
        GetEquipLocHoldGoods(ref equipLocHold);


        // 只能产品质量为： N 的出库
        GetProductNotNV2(ref equipLocHold, productN);


        // 去掉货叉损坏区域后可用货位
        //OLDTODO 【已解决】abnormalForks 单独查异常
        GetEquipLocHaltCrn(ref equipLocHold, abnormalForks);


        // 有货货位去掉任务还没出库完成（立库货位到出库站台位任务）
        var taskGroupCounts = _Task.GetStartLocTaskGroup(); // 任务开始货位汇总数量


        GetEquipLocHoldNoLocTasks(ref equipLocHold, taskGroupCounts);


        return equipLocHold;
    }


    /// <summary>
    /// 获取堆垛机管理货位为空货位(入库)
    /// </summary>
    private void GetEquipLocHoldNoGoods(ref List<POWcsEquipLocHold> equipLocHold, List<PORcsLocation> locations)
    {
        var holds = equipLocHold.Select(t => t.LocId).ToList();
        var locs = locations.Select(t => t.Id).ToList();

        var noGoodLocs = holds.Intersect(locs); // 算交集

        equipLocHold = equipLocHold
            .Join(noGoodLocs, f => f.LocId, s => s, (f, s) => new { Hold = f })
            .Select(t => t.Hold).ToList();
    }

    /// <summary>
    /// 获取堆垛机管理货位为有货位（出库）
    /// </summary>
    private void GetEquipLocHoldGoods(ref List<POWcsEquipLocHold> equipLocHold)
    {
        // var goods = _RcsLocation.GetInStockLocations(EnumInStockType.ExWarehouse);
        var holds = equipLocHold.Select(t => t.LocId.GetValueOrDefault()).ToList();
        var locs = _RcsLocation.GetGoodLocs();// goods.Select(t => t.Id).ToList();
        var ids = holds.Intersect(locs);
        equipLocHold = equipLocHold
            .Join(ids, f => f.LocId, s => s, (f, s) => new { Hold = f })
            .Select(t => t.Hold).ToList();
    }

    private void GetProductNotN(ref List<POWcsEquipLocHold> equipLocHold)
    {
        var locIds = equipLocHold.Select(t => t.LocId.GetValueOrDefault()).ToList();
        var products = _ProdProduct.GetProductIsNByLocIds(locIds);

        equipLocHold = equipLocHold.Join(products, f => f.LocId, s => s.LocId, (f, s) => new
        {
            LocHold = f,
            Product = s,
        }).Select(t => t.LocHold).ToList();
    }


    private void GetProductNotNV2(ref List<POWcsEquipLocHold> equipLocHold, List<ProductLocBean> productN)
    {
        var locIds = equipLocHold.Select(t => t.LocId.GetValueOrDefault()).ToList();
        var products = productN.Where(t => locIds.Contains(t.LocId.GetValueOrDefault())).ToList();

        equipLocHold = equipLocHold.Join(products, f => f.LocId, s => s.LocId, (f, s) => new
        {
            LocHold = f,
            Product = s,
        }).Select(t => t.LocHold).ToList();
    }

    /// <summary>
    /// 空货位去掉任务还没有到达的（到具体立库货位）
    /// </summary>
    /// <param name="equipLocHold"></param>
    private void GetEquipLocHoldNoLocTasks(ref List<POWcsEquipLocHold> equipLocHold,
        List<TaskGroupCount> taskGroupCounts)
    {
        if (taskGroupCounts == null || taskGroupCounts.Count == 0)
        {
            return;
        }
        // 去掉（到具体立库货位）
        //var locIds = taskGroupCounts.Select(t => t.LocId).ToList();
        //equipLocHold = equipLocHold.Where(t => !locIds.Contains(t.LocId)).ToList();
        var tasklocs = equipLocHold.Join(taskGroupCounts, f => f.LocId, s => s.LocId, (f, s) => f).ToList();
        foreach (var item in tasklocs)
        {
            equipLocHold.Remove(item);
        }
    }
    /// <summary>
    /// 空货位去掉任务还没有到达的（到堆垛机站台位）
    /// </summary>
    /// <param name="equipLocHold"></param>
    /// <param name="targetTaskCounts"></param>
    private void GetEquipLocHoldNoTargetTasks(ref List<POWcsEquipLocHold> equipLocHold,
        List<TaskGroupCount> targetTaskCounts, int reserveNum = 0)
    {
        // 堆垛机与放货位关联关系
        var crnInputs = _WcsAsrsCrnInput.GetWcsAsrsCrnInputs();

        // 到达堆垛机的任务
        List<CrnCount> crnCounts = new();
        targetTaskCounts.ForEach(item =>
        {
            var crn = crnInputs.Where(t => t.InputLoc1 == item.LocId || t.InputLoc2 == item.LocId).FirstOrDefault();
            if (crn != null)
            {
                crnCounts.Add(new()
                {
                    CrnId = crn.CrnId,
                    Count = item.Count,
                });
            }
        });

        // 汇总到达堆垛机的输送任务数量
        var crnTasks = crnCounts
            .GroupBy(t => new { t.CrnId })
            .Select(s => new CrnCount { CrnId = s.Key.CrnId, Count = s.Sum(t => t.Count) })
            .ToList();


        // 汇总堆垛机管理空货位数量
        var crnHolds = equipLocHold
            .GroupBy(t => t.EquipId)
            .Select(s => new CrnCount { CrnId = s.Key, Count = s.Count() })
            .ToList();


        // 汇总堆垛机空货位 - 汇总堆垛机任务
        crnHolds.ForEach(item =>
        {
            var task = crnTasks.Where(t => t.CrnId == item.CrnId).FirstOrDefault();
            if (task != null)
            {
                item.Count = item.Count - task.Count - reserveNum; // 预留2个库位
            }
        });
        var ids = crnHolds.Where(t => t.Count > 0).Select(t => t.CrnId).ToList(); // 空货位数量满足

        equipLocHold = equipLocHold.Where(t => ids.Contains(t.EquipId)).ToList();
    }
    /// <summary>
    /// 去掉货叉停机货位
    /// </summary>
    /// <param name="equipLocHold"></param>
    private void GetEquipLocHaltCrn(ref List<POWcsEquipLocHold> equipLocHold, List<long?> abnormalForks)
    {
        if (abnormalForks.Count == 0)
        {
            return;
        }
        // 异常货叉
        var asrsLocHalts = _WcsAsrsLocHalt.GetAsrsLocHalt();

        // 无法到达的货位
        //var locIds = asrsLocHalts.Where(t => abnormalForks.Contains(t.CrnFork)).Select(t => t.LocId).ToList();
        //equipLocHold = equipLocHold.Where(t => !locIds.Contains(t.LocId)).ToList();


        //var locIds = asrsLocHalts.Join(abnormalForks, f => f.CrnFork, s => s, (f, s) => f).Select(t => t.LocId).ToList();
        //var haltloc = equipLocHold.Join(locIds, f => f.LocId, s => s, (f, s) => f).ToList();

        var haltloc = asrsLocHalts.Join(abnormalForks, f => f.CrnFork, s => s, (f, s) => f)
            .Join(equipLocHold, f => f.LocId, s => s.LocId, (f, s) => s).ToList();

        foreach (var item in haltloc)
        {
            equipLocHold.Remove(item);
        }



    }
    private void GetLocHeight(ref List<POWcsEquipLocHold> equipLocHold, List<PORcsLocation> locations, decimal height)
    {
        //equipLocHold = equipLocHold.Join(locations, f => f.LocId, s => s.Id, (f, s) => new
        //{
        //    LocHold = f,
        //    Location = s,
        //}).Where(t => t.Location.LocHeight >= height).Select(t => t.LocHold).ToList();

        var wmsMaterialHeight = _WmsSetting.GetWmsSettingByCode(ConstFields.MaterialHeightSideType);
        _ = int.TryParse(wmsMaterialHeight?.Value, out var settingHeight);
        //如果是高胎(含设置的最小值)
        if (height >= settingHeight)
        {
            equipLocHold = equipLocHold.Join(locations, f => f.LocId, s => s.Id, (f, s) => new
            {
                LocHold = f,
                Location = s,
            }).Where(t => t.Location.LocHeight > settingHeight).Select(t => t.LocHold).ToList();
            //WriteLog($"获取符合条件的高胎货位{equipLocHold.ToJson()}", true);

        }
        else
        {
            equipLocHold = equipLocHold.Join(locations, f => f.LocId, s => s.Id, (f, s) => new
            {
                LocHold = f,
                Location = s,
            }).Where(t => t.Location.LocHeight == settingHeight).Select(t => t.LocHold).ToList();
        }
    }





    public List<WcsWarehouseCrnMaterialInfo> WscSyncWarehouseMaterial()
    {
        var db = Db.Default;
        var rcsEquipCrns = _RcsEquipCrn.GetRcsEquipCrn();

        var wcsEquipLocs = Task.Run(() =>
        {
            // 堆垛机管理货位
            return GetEquipLocHolds();
        });
        var products = Task.Run(() =>
        {
            // 查询可用出库产品（质量N；非冻结；满足最小超期可出库）
            return _ProdProduct.GetWarehousingOutProduct();
        });
        var locs = Task.Run(() =>
        {
            //所有货位信息
            return _RcsLocation.GetAllLocationsISLoc();
        });

        var tasks = Task.Run(() =>
        {
            // 说有出库任务占用货位
            return _Task.GetTaskByType((int)EnumTaskType.Exwarehouse);
        });

        var wcsEquipLocHolds = wcsEquipLocs.Result;
        var rcsLocations = locs.Result.Where(t => t.MaterialId != null).ToList();
        var locProducts = products.Result;
        var taskStartLocs = tasks.Result.Select(t => t.StartLoc);

        // 删除正在出库货位
        wcsEquipLocHolds = wcsEquipLocHolds.Where(t => !taskStartLocs.Contains(t.LocId)).ToList();

        var result = wcsEquipLocHolds.Join(rcsLocations, f => f.LocId, s => s.Id, (f, s) => new
        {
            EquipId = f.EquipId,
            MaterialId = s.MaterialId,
            LocId = s.Id,
            WarehouseId = rcsEquipCrns.Where(t => t.Id == f.EquipId).FirstOrDefault().WarehourseId
        }).Join(locProducts, f => f.LocId, s => s.LocId, (f, s) => new
        {
            f.EquipId,
            f.MaterialId,
            f.WarehouseId,
        }).GroupBy(t => new { t.EquipId, t.MaterialId, t.WarehouseId })
         .Select(t => new WcsWarehouseCrnMaterialInfo
         {
             CrnId = t.Key.EquipId,
             WarehouseId = t.Key.WarehouseId,
             MaterialId = t.Key.MaterialId,
             MaterialCount = t.Count()
         }).ToList();
        return result;


    }

    public void UpdateWarehouseMaterial(List<WcsWarehouseCrnMaterialInfo> wcsWarehouseCrnMaterialInfos)
    {
        var db = Db.Default;
        // TODO 新增或修改 修改数据库多的物料信息 

        var wcsWarehouseCrnMaterialInfos1 = new List<WcsWarehouseCrnMaterialInfo>();
        wcsWarehouseCrnMaterialInfos1.AddRange(wcsWarehouseCrnMaterialInfos);


        //数据库中已存在的数据
        var WarehouseCrnMaterials = db.WcsWarehouseCrnMaterials.ToList();
        WarehouseCrnMaterials.ForEach(item =>
        {
            //传过来的数据是否在现在的数据库中存在
            var Material = wcsWarehouseCrnMaterialInfos1.Where(t => t.WarehouseId == item.WarehouseId && t.CrnId == item.CrnId && t.MaterialId == item.MaterialId).FirstOrDefault();

            int materialCount = 0;
            DateTime updateTime = DateTime.Now;
            if (Material is not null)//有的话 就更新数据 //并且删除此条数据
            {
                materialCount = Material.MaterialCount.GetValueOrDefault();
                wcsWarehouseCrnMaterialInfos1.Remove(Material);
            }
            //如果数量不一致 才会更新数据
            if (item.MaterialCount != materialCount)
            {

                db.WcsWarehouseCrnMaterials.Where(t => t.Id == item.Id).Update(new POWcsWarehouseCrnMaterial
                {
                    MaterialCount = materialCount,
                    UpdateTime = updateTime
                });
                db.SaveChanges();
            }
        });


        //传过来的所有数据
        wcsWarehouseCrnMaterialInfos.ForEach(item =>
        {

            var Material = WarehouseCrnMaterials.Where(t => t.WarehouseId == item.WarehouseId && t.CrnId == item.CrnId && t.MaterialId == item.MaterialId).FirstOrDefault();
            if (Material is null)//数据库中没找到跟数据相同的物料信息 新增
            {
                db.WcsWarehouseCrnMaterials.Insert(new POWcsWarehouseCrnMaterial
                {
                    Id = XID.Nextval,
                    MaterialId = item.MaterialId,
                    WarehouseId = item.WarehouseId,
                    CrnId = item.CrnId,
                    MaterialCount = item.MaterialCount,
                });
            }
            else //如果传过来的数据有这条 则删除
            {
                WarehouseCrnMaterials.Remove(Material);
            }
        });

    }

}